import Context from './context.js';
import _ from 'lodash';

const currentSymbolPrices = new Map();
Context(({ funcGetSymbolDailPrice, funGetSymbols, socket, utils, funGetPositions, openLong, openShort, config, getTrades, getBalance }) => {
  const notice = new utils.Notice({
    token: '9a13ecb3-8414-473f-a207-80e80ef3ad35'
  });
  // 记录每个交易对的K线收盘数据，key为symbol，value为{[klineEndTime]: klineData}
  const closedKlines = {};
  // 记录每个币种上次平仓时间
  const lastTradeTime = {};

  // 判断是否满足建仓条件
  async function canOpenPosition(symbol, Klines) {
    // 建仓
    const nowKline = Klines[0];
    const prevKline = Klines[1];
    const prev2Kline = Klines[2];
    // 判断是否达到开仓的条件
    // 涨幅未达标
    const prevRate = prevKline.rate;
    const prev2Rate = prev2Kline ? prev2Kline.rate : 0;

    // 如果涨幅大于15%，则不建仓
    if (prevKline.rate > 15) {
        return false;
    }
    // 如果前一根K线不达标
    if (prevRate < config.gainTriggerPct) {
      return false;
    }

    // 判断是否在持仓中
    const positions = funGetPositions();
    // 判断当前币种是否在持仓中
    if (positions.some(p => p.symbol === symbol)) {
        return false;
    }

    // 判断是否在冷静期
    if (lastTradeTime[symbol]) {
      const diff = (new Date().getTime() - lastTradeTime[symbol]) / 60000;
      if (diff < config.cooldownMinutes) {
        console.log(['冷静期', symbol, diff]);
        return false;
      }
    }
    // 判断 8 个小时内的亏损次数
    const trades = getTrades();
    const symbolTrades = _.get(trades, `symbols.${symbol}`, []);
    // 8小时内的亏损次数
    const lossTimes = symbolTrades.filter(t => t.rp < 0 &&
      t.timestamp > new Date().getTime() - 1000 * 60 * 60 * 8).length;
    // 如果亏损次数大于等于2次，则不建仓
    if (lossTimes >= 2) {
      console.log(['lossTimes', lossTimes, symbol]);
      return false;
    }
    // 预加载当天的开盘价
    const dailPrices = await funcGetSymbolDailPrice(symbol);
    // 如果当天的开盘价超过20%的涨幅，则不建仓
    if (dailPrices && dailPrices.length && (nowKline.close - dailPrices[0].open) / dailPrices[0].open * 100 > 20) {
      return false;
    }

    // 回落超过 5 个点
    if (prevKline.closeToHigh > 5) {
      // return false;
    }

    await utils.predict(symbol, '3m', 120, prevKline.time, async (info) => {
      let side = info?.action;
      if (!side) {
        return;
      }
      console.log(`ai 预测结果: ${symbol} ${side} -` + prevKline.time);
      side = side.toUpperCase();
      // 建仓
      const quantity = getBalance() * 1 / nowKline.close;
      let openResult = null;
      let takeProfitPct = info?.take_profit_percent || config.takeProfitPct;
      takeProfitPct = config.takeProfitPct;
      let stopLossPct = info?.stop_loss_percent ? (info?.stop_loss_percent * 2.5) : config.stopLossPct;
      if (side === 'LONG') {
        return false;
        // openResult = await openLong(symbol, quantity, takeProfitPct, stopLossPct);
      } else if (side === 'SHORT') {
        const dailPrices = await funcGetSymbolDailPrice(symbol);
        // 检查是否成功获取到数据
        if (!dailPrices || !Array.isArray(dailPrices)) {
            return false;
        }
        // 0 时区涨幅大于20% 不建仓
        if ((nowKline.close - dailPrices[0].open) / dailPrices[0].open * 100 > 20) {
          return false;
        }
        const currentPrice = currentSymbolPrices.get(symbol).close;
        const predictedPrice = nowKline.close;

        // 可接受的回落幅度（0.3%）
        const maxDropAllowed = 0.003 * currentPrice;

        // 如果价格已经下跌超过容忍度，说明可能追空风险大，放弃做空
        if (currentPrice < predictedPrice && (predictedPrice - currentPrice) > maxDropAllowed) {
          // return false;
        }
        let lastCurrentPrice = currentPrice;
        // 如果当前价格大于预测价格，判断合适入的时机
        if (currentPrice >= predictedPrice && (currentPrice - predictedPrice) > maxDropAllowed) {
          // 看看30s 后价格是否比当前价格高
          let times = 0;
          do {
            await utils.sleep(1000 * 60); // 等待60秒
            // 当前价在涨
            if (currentSymbolPrices.get(symbol).close >= lastCurrentPrice) {
              lastCurrentPrice = currentSymbolPrices.get(symbol).close;
              times = 0;
            } else {
              times++;
            }
            // 连续下跌3次
            if (times > 3) {
              break;
            }
          } while (true);
        }
        openResult = await openShort(symbol, quantity, takeProfitPct, stopLossPct);
      } else {
        return false;
      }
      notice.bot.sendMsg(`[${config.name}]: ${symbol} ${side} ${prevKline.time}  [${stopLossPct}, ${takeProfitPct}]`);
      // 添加冷静期
      if (openResult) {
        lastTradeTime[symbol] = new Date().getTime();
      }
    });
  }

  // K线数据处理函数
  let onKline = async (data) => {
    const { symbol, k } = data;
    const now = Number(k.t);
    // 初始化该symbol的K线收盘数据
    closedKlines[symbol] = closedKlines[symbol] || {};

    // 记录本根K线的收盘数据，key为K线结束时间
    closedKlines[symbol][k.t] = {
      open: data.open,
      close: data.close,
      high: data.high,
      time: data.time,
      rate: data.rate,
      closeToHigh: data.closeToHigh,
    };
    currentSymbolPrices.set(symbol, closedKlines[symbol][k.t]);
    // 按时间倒序排序
    const times = Object.keys(closedKlines[symbol]).map(Number).sort((a, b) => b - a);

    // 没有持仓时，尝试建仓（必须有上一根K线的收盘数据）
    if (times.length >= 2) {
      await canOpenPosition(symbol, times.map(i => closedKlines[symbol][i]));
    }

    // 只保留最近5根K线，防止内存泄漏（从后往前删）
    if (times.length > 5) {
      const removeCount = times.length - 5;
      for (let i = 0; i < removeCount; i++) {
        delete closedKlines[symbol][times[times.length - 1 - i]];
      }
    }
  };
  // 记录已加载的币种
  let loadedSymbols = {};
  let loadedSymbolsTimer = null;
  let loadSymbols = async () => {
    console.log('loadSymbols ' + new Date().toISOString());
    const symbols = await funGetSymbols();
    // 找出未加载的币种
    const ts = symbols.filter(i => !loadedSymbols[i.symbol]);
    console.log(`loadSymbols: new ${ts.length} symbols`);
    (_.chunk(ts.map(i => `${i.symbol.toLowerCase()}_perpetual@continuousKline_3m`), 200))
      .forEach((chunk, i) => {
        // 发送订阅 continuousKline_3m 数据的请求
        socket.send(JSON.stringify({
          method: 'SUBSCRIBE',
          params: chunk,
          id: _.now() + i,
        }));
      });
    if (loadedSymbolsTimer) {
      clearTimeout(loadedSymbolsTimer);
    }
    // 1小时后重新加载
    loadedSymbolsTimer = setTimeout(loadSymbols, 1000 * 60 * 60);
  }
  // WebSocket 连接打开时的回调
  socket.on('open', async () => {
    await loadSymbols();
  });
  // WebSocket 收到消息时的回调
  socket.on('message', async function (data) {
    try {
      const info = JSON.parse(data);
      // 判断是否为 continuous_kline 类型的数据流
      if (info.stream && info.data.e === 'continuous_kline') {
        if (info.data.k.i !== '3m') {
          return;
        }
        const symbol = info.data.ps; // 交易对
        const rate = (info.data.k.c - info.data.k.o) / info.data.k.o * 100;
        loadedSymbols[symbol] = true;
        const time = utils.formatBeijingDateTime(new Date(info.data.k.t));
        const high = info.data.k.h;
        const close = info.data.k.c;
        const open = info.data.k.o;
        const closeToHigh = ((high - close) / open) * 100;
        await onKline({ ...info.data, symbol, rate, time, closeToHigh, high, close, open });
      } else {
        console.log(info);
      }
    } catch (error) {
      // 解析消息出错，打印错误信息
      console.error(new Date().toISOString(), 'Error parsing message:', error);
    }
  });
});
